﻿#include "StdAfx.h"
#include "UIBlue.h"

/////////////////////////////////////////////////////////////////////////////////////
//

#ifndef BlendRGB
   #define BlendRGB(c1, c2, factor) \
      RGB( GetRValue(c1) + ((GetRValue(c2) - GetRValue(c1)) * factor / 100L), \
           GetGValue(c1) + ((GetGValue(c2) - GetGValue(c1)) * factor / 100L), \
           GetBValue(c1) + ((GetBValue(c2) - GetBValue(c1)) * factor / 100L) )
#endif

/////////////////////////////////////////////////////////////////////////////////////
//

CRenderClip::~CRenderClip() {
	ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC);
	ASSERT(::GetObjectType(hRgn)==OBJ_REGION);
	ASSERT(::GetObjectType(hOldRgn)==OBJ_REGION);
	::SelectClipRgn(hDC, hOldRgn);
	::DeleteObject(hOldRgn);
	::DeleteObject(hRgn);
}

/////////////////////////////////////////////////////////////////////////////////////
//
//

void CBlueRenderEngineUI::GenerateClip(HDC hDC, RECT rcItem, CRenderClip& clip) {
	RECT rcClip = { 0 };
	::GetClipBox(hDC, &rcClip);
	clip.hOldRgn = ::CreateRectRgnIndirect(&rcClip);
	clip.hRgn = ::CreateRectRgnIndirect(&rcItem);
	::ExtSelectClipRgn(hDC, clip.hRgn, RGN_AND);
	clip.hDC = hDC;
	clip.rcItem = rcItem;
}

void CBlueRenderEngineUI::DoFillRect(HDC hDC, CPaintManagerUI* pManager, RECT rcItem, UITYPE_COLOR Color) {
	DoFillRect(hDC, pManager, rcItem, pManager->GetThemeColor(Color));
}

void CBlueRenderEngineUI::DoFillRect(HDC hDC, CPaintManagerUI* pManager, RECT rcItem, COLORREF clrFill) {
	ASSERT(::GetObjectType(hDC) == OBJ_DC || ::GetObjectType(hDC) == OBJ_MEMDC);
	::SetBkColor(hDC, clrFill);
	::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, NULL, 0, NULL);
}

void CBlueRenderEngineUI::DoPaintPrettyText(HDC hDC, CPaintManagerUI* pManager, RECT& rc, LPCTSTR pstrText, UITYPE_COLOR iTextColor, UITYPE_COLOR iBackColor, UINT uStyle) {
	ASSERT(::GetObjectType(hDC)==OBJ_DC || ::GetObjectType(hDC)==OBJ_MEMDC);

	if (::IsRectEmpty(&rc)) {
		return;
	}
	// LabelPanel uStyle 0x00000004|0x00000020 = 36
	// DT_CENTER                   0x00000001 1
	// DT_RIGHT                    0x00000002 2
	// DT_VCENTER                  0x00000004 4
	// DT_SINGLELINE               0x00000020 32
	// DT_CALCRECT                 0x00000400 1024
	bool bDraw = (uStyle & DT_CALCRECT) == 0; // true

	RECT rcClip = { 0 };
	::GetClipBox(hDC, &rcClip); // 获取要刷新的区域
	HRGN hOldRgn = ::CreateRectRgnIndirect(&rcClip); // 创建矩形区域
	HRGN hRgn = ::CreateRectRgnIndirect(&rc); // 创建矩形区域
	// +rcClip{ top = 0 bottom = 720 left = 0 right = 1424 }	tagRECT
	// +rc{ top = 0 bottom = 720 left = 0 right = 1424 }	tagRECT &
	if (bDraw) {
		::ExtSelectClipRgn(hDC, hRgn, RGN_AND); // 新的剪辑区域将当前剪切区域的重叠区域与 hrgn 标识的区域组合在一起。
	}

	HFONT hOldFont = (HFONT) ::SelectObject(hDC, pManager->GetThemeFont(UIFONT_NORMAL));
	::SetBkMode(hDC, TRANSPARENT);
	::SetTextColor(hDC, pManager->GetThemeColor(iTextColor));

	// If the drawstyle includes an alignment, we'll need to first determine the text-size so
	// we can draw it at the correct position...
	// LabelPanel uStyle 0x00000004|0x00000020 = 36
	// DT_CENTER                   0x00000001 1
	// DT_RIGHT                    0x00000002 2
	// DT_VCENTER                  0x00000004 4
	// DT_SINGLELINE               0x00000020 32
	// DT_CALCRECT                 0x00000400 1024
	// 第一次 uStyle = DT_SINGLELINE|DT_VCENTER = (0x00000020|0x00000004) = 36
	// (DT_SINGLELINE|DT_VCENTER)& DT_SINGLELINE = 32
	// (DT_SINGLELINE|DT_VCENTER)& DT_VCENTER = 4 (0x00000020|0x00000004)&0x00000004
	// (DT_SINGLELINE|DT_VCENTER)& DT_RIGHT = 0 (0x00000020|0x00000004)&0x00000002
	// (DT_SINGLELINE|DT_VCENTER)& DT_CALCRECT = 0

	// 第二次 uStyle = DT_SINGLELINE|DT_VCENTER| DT_CALCRECT = (0x00000020|0x00000004|0x00000400) = 1024
	// (DT_SINGLELINE|DT_VCENTER| DT_CALCRECT)& DT_SINGLELINE = 32 (0x00000020|0x00000004|0x00000400)&0x00000020
	// (DT_SINGLELINE|DT_VCENTER| DT_CALCRECT)& DT_VCENTER = 4  (0x00000020|0x00000004|0x00000400)&0x00000004
	// (DT_SINGLELINE|DT_VCENTER| DT_CALCRECT)& DT_CENTER = 0  (0x00000020|0x00000004|0x00000400)&0x00000001
	// (DT_SINGLELINE|DT_VCENTER| DT_CALCRECT)& DT_RIGHT = 0  (0x00000020|0x00000004|0x00000400)&0x00000002
	// (DT_SINGLELINE|DT_VCENTER| DT_CALCRECT)& DT_CALCRECT = 1024 (0x00000020|0x00000004|0x00000400)&0x00000400
	if ((uStyle & DT_SINGLELINE) != 0 && (uStyle & DT_VCENTER) != 0 && (uStyle & DT_CALCRECT) == 0) {
		RECT rcText = { 0, 0, 9999, 100 };
		// +rcText{ top = 0 bottom = 100 left = 0 right = 9999 }	tagRECT
		DoPaintPrettyText(hDC, pManager, rcText, pstrText, iTextColor, iBackColor, uStyle | DT_CALCRECT);
		rc.top = rc.top + ((rc.bottom - rc.top) / 2) - ((rcText.bottom - rcText.top) / 2);
		rc.bottom = rc.top + (rcText.bottom - rcText.top);
	}
	if ((uStyle & DT_SINGLELINE) != 0 && (uStyle & DT_CENTER) != 0 && (uStyle & DT_CALCRECT) == 0) {
		RECT rcText = { 0, 0, 9999, 100 };
		DoPaintPrettyText(hDC, pManager, rcText, pstrText, iTextColor, iBackColor, uStyle | DT_CALCRECT);
		::OffsetRect(&rc, (rc.right - rc.left) / 2 - (rcText.right - rcText.left) / 2, 0);
	}
	if ((uStyle & DT_SINGLELINE) != 0 && (uStyle & DT_RIGHT) != 0 && (uStyle & DT_CALCRECT) == 0) {
		RECT rcText = { 0, 0, 9999, 100 };
		DoPaintPrettyText(hDC, pManager, rcText, pstrText, iTextColor, iBackColor, uStyle | DT_CALCRECT);
		rc.left = rc.right - (rcText.right - rcText.left);
	}

	// Paint backhground
	if (iBackColor != UICOLOR__INVALID) {
		DoFillRect(hDC, pManager, rc, iBackColor);
	}

	TEXTMETRIC tm = pManager->GetThemeFontInfo(UIFONT_NORMAL);
	POINT pt = { rc.left, rc.top };
	int cyLine = tm.tmHeight + tm.tmExternalLeading;
	int cyMinHeight = 0;

    while ( *pstrText != '\0' ) {
        POINT ptPos = pt;
        int cchChars = 0;
        LPCTSTR p = pstrText;
        SIZE szText = { 0 };
        while ( *p != '\0' && *p != '<' && *p != '\n' && *p != '&' ) {
            // This part makes sure that we're word-wrapping if needed or providing support
            // for DT_END_ELLIPSIS. Unfortunately the GetTextExtentPoint32() call is pretty
            // slow when repeated so often.
            // TODO: Rewrite and use GetTextExtentExPoint() instead!
            cchChars++;
            szText.cx = cchChars * tm.tmMaxCharWidth; // tm.tmMaxCharWidth = 29
            if ( pt.x + szText.cx >= rc.right ) {
				// 计算指定文本字符串的宽度和高度
               ::GetTextExtentPoint32(hDC, pstrText, cchChars, &szText);
            }
			if (pt.x + szText.cx >= rc.right) {
				break;
			}
            p = ::CharNext(p);
        }
        if ( cchChars > 0 ) {
			::GetTextExtentPoint32(hDC, pstrText, cchChars, &szText);
            if ( bDraw ) {
               ::TextOut(hDC, ptPos.x, ptPos.y, pstrText, cchChars);
			}
			pt.x += szText.cx;
            pstrText += cchChars; // 向后移 cchChars 个字节
        }
    }

	// Return size of text when requested
	if ((uStyle & DT_CALCRECT) != 0) {
		rc.bottom = MAX(cyMinHeight, pt.y + cyLine);
		if (rc.right >= 9999) {
			if (_tcslen(pstrText) > 0) pt.x += 3;
			rc.right = pt.x;
		}
	}

	if (bDraw) {
		::SelectClipRgn(hDC, hOldRgn);
	}

	::DeleteObject(hOldRgn);
	::DeleteObject(hRgn);

	::SelectObject(hDC, hOldFont);
}

void CBlueRenderEngineUI::DoPaintButton(HDC hDC, CPaintManagerUI* pManager, RECT rc, LPCTSTR pstrText, RECT rcPadding, UINT uState, UINT uStyle) {
	ASSERT(::GetObjectType(hDC) == OBJ_DC || ::GetObjectType(hDC) == OBJ_MEMDC);
	// Draw focus rectangle
	if (((uState & UISTATE_FOCUSED) != 0) && pManager->GetSystemSettings().bShowKeyboardCues) {
		CBlueRenderEngineUI::DoPaintFrame(hDC, pManager, rc, UICOLOR_BUTTON_BORDER_FOCUS, UICOLOR_BUTTON_BORDER_FOCUS, UICOLOR__INVALID, UIFRAME_ROUND);
		::InflateRect(&rc, -1, -1);
	}
	
	// Draw frame and body
	COLORREF clrColor1, clrColor2;
	UITYPE_COLOR clrBorder1, clrBorder2, clrText, clrBack;
	if ((uState & UISTATE_DISABLED) != 0) {
		clrBorder1 = UICOLOR_BUTTON_BORDER_DISABLED;
		clrBorder2 = UICOLOR_BUTTON_BORDER_DISABLED;
		clrText = UICOLOR_BUTTON_TEXT_DISABLED;
		clrBack = UICOLOR_BUTTON_BACKGROUND_DISABLED;
	} else if ((uState & UISTATE_PUSHED) != 0) {
		clrBorder1 = UICOLOR_BUTTON_BORDER_DARK;
		clrBorder2 = UICOLOR_BUTTON_BORDER_LIGHT;
		clrText = UICOLOR_BUTTON_TEXT_PUSHED;
		clrBack = UICOLOR_BUTTON_BACKGROUND_PUSHED;
	} else {
		clrBorder1 = UICOLOR_BUTTON_BORDER_LIGHT;
		clrBorder2 = UICOLOR_BUTTON_BORDER_DARK;
		clrText = UICOLOR_BUTTON_TEXT_NORMAL;
		clrBack = UICOLOR_BUTTON_BACKGROUND_NORMAL;
	}
	// Draw button 画按钮边框
	DoPaintFrame(hDC, pManager, rc, clrBorder1, clrBorder2, UICOLOR__INVALID, UIFRAME_ROUND);
	::InflateRect(&rc, -1, -1);
	
	/*
	// The pushed button has an inner light shade 阴影？
	if ((uState & UISTATE_PUSHED) != 0) {
		DoPaintFrame(hDC, pManager, rc, UICOLOR_STANDARD_LIGHTGREY, UICOLOR_STANDARD_LIGHTGREY, UICOLOR__INVALID);
		rc.top += 1;
		rc.left += 1;
	}
	*/

	// Gradient background 画一个按钮背景渐变色
	pManager->GetThemeColorPair(clrBack, clrColor1, clrColor2);
	DoPaintGradient(hDC, pManager, rc, clrColor1, clrColor2, true, 32);

	/*
	// Draw hightlight inside button 在按钮的里画一个左右下的内边框
	::SelectObject(hDC, pManager->GetThemePen(UICOLOR_DIALOG_BACKGROUND));
	POINT ptTemp;
	::MoveToEx(hDC, rc.left, rc.top, &ptTemp);
	::LineTo(hDC, rc.left, rc.bottom - 1);
	::LineTo(hDC, rc.right - 2, rc.bottom - 1);
	::LineTo(hDC, rc.right - 2, rc.top);
	*/
	// Draw text
	RECT rcText = rc;
	::InflateRect(&rcText, -1, -1);
	rcText.left += rcPadding.left;
	rcText.top += rcPadding.top;
	rcText.right -= rcPadding.right;
	rcText.bottom -= rcPadding.bottom;
	DoPaintPrettyText(hDC, pManager, rcText, pstrText, clrText, UICOLOR__INVALID, DT_SINGLELINE | uStyle);
}

void CBlueRenderEngineUI::DoPaintGradient(HDC hDC, CPaintManagerUI* pManager, RECT rc, COLORREF clrFirst, COLORREF clrSecond, bool bVertical, int nSteps)
{
	typedef BOOL(WINAPI *PGradientFill)(HDC, PTRIVERTEX, ULONG, PVOID, ULONG, ULONG);
	static PGradientFill lpGradientFill = (PGradientFill) ::GetProcAddress(::GetModuleHandle("msimg32.dll"), "GradientFill");
	if (lpGradientFill != NULL) {
		// Use Windows gradient function from msimg32.dll
		// It may be slower than the code below but makes really pretty gradients on 16bit colors.
		TRIVERTEX triv[2] =
		{
			{ rc.left, rc.top, GetRValue(clrFirst) << 8, GetGValue(clrFirst) << 8, GetBValue(clrFirst) << 8, 0xFF00 },
			{ rc.right, rc.bottom, GetRValue(clrSecond) << 8, GetGValue(clrSecond) << 8, GetBValue(clrSecond) << 8, 0xFF00 }
		};
		GRADIENT_RECT grc = { 0, 1 };
		lpGradientFill(hDC, triv, 2, &grc, 1, bVertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H);
	} else {
		// Determine how many shades
		int nShift = 1;
		if (nSteps >= 64) nShift = 6;
		else if (nSteps >= 32) nShift = 5;
		else if (nSteps >= 16) nShift = 4;
		else if (nSteps >= 8) nShift = 3;
		else if (nSteps >= 4) nShift = 2;
		int nLines = 1 << nShift;
		for (int i = 0; i < nLines; i++) {
			// Do a little alpha blending
			BYTE bR = (BYTE)((GetRValue(clrSecond) * (nLines - i) + GetRValue(clrFirst) * i) >> nShift);
			BYTE bG = (BYTE)((GetGValue(clrSecond) * (nLines - i) + GetGValue(clrFirst) * i) >> nShift);
			BYTE bB = (BYTE)((GetBValue(clrSecond) * (nLines - i) + GetBValue(clrFirst) * i) >> nShift);
			// ... then paint with the resulting color
			HBRUSH hBrush = ::CreateSolidBrush(RGB(bR, bG, bB));
			RECT r2 = rc;
			if (bVertical) {
				r2.bottom = rc.bottom - ((i * (rc.bottom - rc.top)) >> nShift);
				r2.top = rc.bottom - (((i + 1) * (rc.bottom - rc.top)) >> nShift);
				if ((r2.bottom - r2.top) > 0) ::FillRect(hDC, &r2, hBrush);
			}
			else {
				r2.left = rc.right - (((i + 1) * (rc.right - rc.left)) >> nShift);
				r2.right = rc.right - ((i * (rc.right - rc.left)) >> nShift);
				if ((r2.right - r2.left) > 0) ::FillRect(hDC, &r2, hBrush);
			}
			::DeleteObject(hBrush);
		}
	}
}

void CBlueRenderEngineUI::DoPaintFrame(HDC hDC, CPaintManagerUI* pManager, RECT rcItem, UITYPE_COLOR Light, UITYPE_COLOR Dark, UITYPE_COLOR Background, UINT uStyle) {
	if (Background != UICOLOR__INVALID) {
		DoFillRect(hDC, pManager, rcItem, Background);
	}
	if ((uStyle & UIFRAME_ROUND) != 0) {
		POINT ptTemp;
		::SelectObject(hDC, pManager->GetThemePen(Light));
		::MoveToEx(hDC, rcItem.left, rcItem.bottom - 2, &ptTemp);
		::LineTo(hDC, rcItem.left, rcItem.top + 1);
		::LineTo(hDC, rcItem.left + 1, rcItem.top);
		::LineTo(hDC, rcItem.right - 2, rcItem.top);
		::SelectObject(hDC, pManager->GetThemePen(Dark));
		::LineTo(hDC, rcItem.right - 1, rcItem.top + 1);
		::LineTo(hDC, rcItem.right - 1, rcItem.bottom - 2);
		::LineTo(hDC, rcItem.right - 2, rcItem.bottom - 1);
		::LineTo(hDC, rcItem.left, rcItem.bottom - 1);
	}
	if ((uStyle & UIFRAME_FOCUS) != 0) {
		HPEN hPen = ::CreatePen(PS_DOT, 1, pManager->GetThemeColor(UICOLOR_BUTTON_BORDER_FOCUS));
		HPEN hOldPen = (HPEN) ::SelectObject(hDC, hPen);
		POINT ptTemp;
		::MoveToEx(hDC, rcItem.left, rcItem.bottom - 1, &ptTemp);
		::LineTo(hDC, rcItem.left, rcItem.top);
		::LineTo(hDC, rcItem.right - 1, rcItem.top);
		::LineTo(hDC, rcItem.right - 1, rcItem.bottom - 1);
		::LineTo(hDC, rcItem.left, rcItem.bottom - 1);
		::SelectObject(hDC, hOldPen);
		::DeleteObject(hPen);
	} else {
		POINT ptTemp;
		::SelectObject(hDC, pManager->GetThemePen(Light));
		::MoveToEx(hDC, rcItem.left, rcItem.bottom - 1, &ptTemp);
		::LineTo(hDC, rcItem.left, rcItem.top);
		::LineTo(hDC, rcItem.right - 1, rcItem.top);
		::SelectObject(hDC, pManager->GetThemePen(Dark));
		::LineTo(hDC, rcItem.right - 1, rcItem.bottom - 1);
		::LineTo(hDC, rcItem.left, rcItem.bottom - 1);
	}
}